#ifndef SATURNOS_INC_KERN_VM_H
#define SATURNOS_INC_KERN_VM_H

#include <types.h>
#include <kern/proc.h>
#include <kern/mmu.h>

/**
* initiate GDT for the current processor.
* must be invoked by all processors
**/
void vm_init_gdt(void);

/**
* initiate virtual memory
**/
void vm_init(void);

/**
* allocate a page from the free list
* @param pa - output: the physical address of the allocated page, corresponding
*             page_t has 0 in its ref count
*
* @return 0 for success
*         -ENOMEM if free list is empty
**/
int vm_insert_page(pte_t* pgdir, uintptr_t va, int perm);

/**
* check if a virtual address is mapped to any physical address
*
* @param pgdir - input: the page dir in which to look
* @param va - input: the virtual address
*
* @return 0 for not mapped 1 for mapped
*/
int vm_is_mapped(pde_t* pgdir, uintptr_t va);

/**
* allocates a new page an map it to an available kernel va
* page will have a ref count of 1
*/
void* vm_kalloc_page();

void vm_retain_kvpage(void* kpg) __attribute__ ((nonnull (1)));
void vm_release_kvpage(void* kpg) __attribute__ ((nonnull (1)));

/**
* initiate a page dir
*/
void vm_init_pgdir(pde_t* pgdir) __attribute__ ((nonnull (1)));

/**
* maps a physical page address at a given virtual address. allocates pgtbl if
* necessary. will result in an inc in page ref count.
*
* panics if:
*  va or pa is not aligned to PGSIZE
*  
* error outcome if:
*  va is already mapped
*
* @return 0 for success
*         -ENOMEM if pgtbl allocation failed 
*/
int vm_map(pde_t* pgdir, uintptr_t va, physaddr_t pa, int perm);

/** 
* calculates the physical address va is mapping
*
* @param va - input: the requested va
*
*/
physaddr_t vm_va2pa(pde_t* pgdir, uintptr_t va);

/**
* unmaps a page from the virtual address. will result in dec pa ref count.
*
* panics if
*  va is not mapped
*
* @param va - input: virtual address to unmap
* 
* @return 0 for success
*/
void vm_unmap(pde_t* pgdir, uintptr_t va);

/**
* sets permissions to a page.
* use PTE_U and PTE_W for permissions.
*
* panic if
*  va is not mapped
*
* error outcome if
*  perm contains flags other than U and W
*
*/
void vm_set_perm(pde_t* pgdir, uintptr_t va, int perm);

/**
* gets the va permissions
*
* @param va - input: the virtual address
* @param perm - output: the va permissions
*
* panic if
*  va is not mapped
*
* @return permissions
*/
int vm_get_perm(pde_t* pgdir, uintptr_t va);

/**
* switches pgdir and tss if needed
*/
void vm_swtch_uvm(void);

int vm_is_kern_addr(uintptr_t va);

#endif /* SATURNOS_INC_KERN_VM_H */

